// (C) Copyright 2012 Kystar. All rights reserved.

`timescale 1ns/100ps
`default_nettype none

module net_vob_top
#(parameter
    MAX_VIN_WIDTH = 0,
    FRAME1_START_ADDR = 0,
    DISP_DATA_FIFO_DEPTH = 0,
    DISP_DATA_FIFO_DEPTH_BW = 0
)
(
    // global signals
    input  wire          I_sclk,
    input  wire          I_rst_n,

    // control
    input  wire          I_new_frame,
    input  wire          I_vib_use_c1,

    // memory interface
    output reg           O_sdm1_vob_req,
    input  wire          I_sdm1_vob_ack,
    input  wire          I_sdm1_sdram_wr_rd_end,
    output reg           O_sdm1_sdram_rd_en,
    output reg  [ 31: 0] O_sdm1_sdram_start_addr,
    output reg  [ 15: 0] O_sdm1_sdram_length,
    input  wire [ 23: 0] I_sdm1_sdram_rdata,
    input  wire          I_sdm1_sdram_rdata_valid,

    output reg           O_sdm2_vob_req,
    input  wire          I_sdm2_vob_ack,
    input  wire          I_sdm2_sdram_wr_rd_end,
    output reg           O_sdm2_sdram_rd_en,
    output reg  [ 31: 0] O_sdm2_sdram_start_addr,
    output reg  [ 15: 0] O_sdm2_sdram_length,
    input  wire [ 23: 0] I_sdm2_sdram_rdata,
    input  wire          I_sdm2_sdram_rdata_valid,

    //
    input  wire [ DISP_DATA_FIFO_DEPTH_BW - 1: 0] I_out_fifo_usedw,
    output reg           O_out_fifo_wrreq,
    output reg  [ 23: 0] O_out_fifo_wdata,

    // registers
    input  wire [ 12: 0] I_reg_rb_vin_width,
    input  wire          I_reg_px_enable,
    input  wire [ 11: 0] I_reg_px_start_row,
    input  wire [ 11: 0] I_reg_px_start_col,
    input  wire [ 11: 0] I_reg_px_width,
    input  wire [ 11: 0] I_reg_px_height,
    input  wire [ 11: 0] I_reg_px_line_step
);

/******************************************************************************
                                <localparams>
******************************************************************************/
localparam MAX_RD_LENTGH = 256;

localparam // rd_state
    RD_IDLE = 0,
    RD_INIT = 1,
    RD_LINE_PRE1 = 1<<1,
    RD_LINE_PRE2 = 1<<2,
    RD_LINE = 1<<3,
    RD_LINE_POST1 = 1<<4,
    RD_LINE_POST2 = 1<<5,
    RD_IF_END = 1<<6;

localparam // rd_line_state
    RL_IDLE = 0,
    RL_INIT = 1,
    RL_WAIT_FIFO = 1<<1,
    RL_REQ_MEM = 1<<2,
    RL_START = 1<<3,
    RL_READING = 1<<4,
    RL_POST = 1<<5,
    RL_IF_FINISH = 1<<6,
    RL_END = 1<<7;

/******************************************************************************
                              <internal signals>
******************************************************************************/
reg  [ 11: 0] line_cnt;
reg  rd_line_start;
reg  rd_line_end;
reg  [ 31: 0] line_start_addr;
reg  [ 11: 0] left_pixels;
reg  [ 6: 0] rd_state;
reg  [ 6: 0] next_rd_state;
reg  [ 7: 0] rd_line_state;
reg  [ 7: 0] next_rd_line_state;
reg  vib_use_c1;
wire sdram_wr_rd_end;
reg  [ 11: 0] current_length;
reg  read_sdm2;
wire [ 11: 0] reg_px_line_step;
reg  [ 11: 0] row;
reg  [ 11: 0] next_round_start_row;
reg  add_next_round_start_row;

/******************************************************************************
                                <module body>
******************************************************************************/
assign reg_px_line_step = I_reg_px_line_step == 'd0 ? 'd1 : I_reg_px_line_step;

//--------------------------------------------------------------------
// memory mux
//--------------------------------------------------------------------
always @(posedge I_sclk)
    if (I_new_frame)
        vib_use_c1 <= I_vib_use_c1;

//--------------------------------------------------------------------
// state machine : rd_state
//--------------------------------------------------------------------
always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        rd_state <= RD_IDLE;
    else if (I_new_frame && I_reg_px_enable)
        rd_state <= RD_INIT;
    else
        rd_state <= next_rd_state;

always @(*)
    case (rd_state)
        RD_IDLE:
            next_rd_state = RD_IDLE;
        RD_INIT:
            next_rd_state = RD_LINE_PRE1;
        RD_LINE_PRE1:
            next_rd_state = RD_LINE_PRE2;
        RD_LINE_PRE2:
            next_rd_state = RD_LINE;
        RD_LINE:
            if (rd_line_end)
                next_rd_state = RD_LINE_POST1;
            else
                next_rd_state = RD_LINE;
        RD_LINE_POST1:
            next_rd_state = RD_LINE_POST2;
        RD_LINE_POST2:
            next_rd_state = RD_IF_END;
        RD_IF_END:
            if (line_cnt == I_reg_px_height)
                next_rd_state = RD_IDLE;
            else
                next_rd_state = RD_LINE_PRE1;
        default:
            next_rd_state = RD_IDLE;
    endcase

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        line_cnt <= 'd0;
    else if (I_new_frame)
        line_cnt <= 'd0;
    else if (rd_state == RD_IDLE)
        line_cnt <= 'd0;
    else if (rd_state == RD_LINE_POST2)
        line_cnt <= line_cnt + 1;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        rd_line_start <= 1'b0;
    else if (I_new_frame)
        rd_line_start <= 1'b0;
    else
        rd_line_start <= rd_state == RD_LINE_PRE2;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        row <= 'd0;
    else if (rd_state == RD_INIT)
        row <= I_reg_px_start_row;
    else if (rd_state == RD_LINE_POST1)
        row <= row + reg_px_line_step;
    else if (rd_state == RD_LINE_POST2)
        begin
        if (row >= I_reg_px_start_row + I_reg_px_height)
            row <= next_round_start_row;
        end

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        add_next_round_start_row <= 1'b0;
    else
        add_next_round_start_row <= (rd_state == RD_LINE_POST2 && (row >= I_reg_px_start_row + I_reg_px_height));

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        next_round_start_row <= 'd1;
    else if (rd_state == RD_INIT)
        next_round_start_row <= I_reg_px_start_row + 1'b1;
    else if (add_next_round_start_row)
        next_round_start_row <= next_round_start_row + 1'b1;

always @(posedge I_sclk)
    line_start_addr <= row[11:1] * I_reg_rb_vin_width + I_reg_px_start_col;

always @(posedge I_sclk)
    read_sdm2 <= row[0];

//--------------------------------------------------------------------
// state machine : rd_line_state
//--------------------------------------------------------------------
assign sdram_wr_rd_end = read_sdm2 ? I_sdm2_sdram_wr_rd_end : I_sdm1_sdram_wr_rd_end;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        rd_line_state <= RL_IDLE;
    else if (I_new_frame && I_reg_px_enable)
        rd_line_state <= RL_IDLE;
    else
        rd_line_state <= next_rd_line_state;

always @(*)
    case (rd_line_state)
        RL_IDLE:
            if (rd_line_start)
                next_rd_line_state = RL_INIT;
            else
                next_rd_line_state = RL_IDLE;
        RL_INIT:
            next_rd_line_state = RL_WAIT_FIFO;
        RL_WAIT_FIFO:
            if (I_out_fifo_usedw < DISP_DATA_FIFO_DEPTH - current_length)
                next_rd_line_state = RL_REQ_MEM;
            else
                next_rd_line_state = RL_WAIT_FIFO;
        RL_REQ_MEM:
            if (read_sdm2 ? I_sdm2_vob_ack : I_sdm1_vob_ack)
                next_rd_line_state = RL_START;
            else
                next_rd_line_state = RL_REQ_MEM;
        RL_START:
            next_rd_line_state = RL_READING;
        RL_READING:
            if (sdram_wr_rd_end)
                next_rd_line_state = RL_POST;
            else
                next_rd_line_state = RL_READING;
        RL_POST:
            next_rd_line_state = RL_IF_FINISH;
        RL_IF_FINISH:
            if (left_pixels == 'd0)
                next_rd_line_state = RL_END;
            else
                next_rd_line_state = RL_WAIT_FIFO;
        RL_END:
            next_rd_line_state = RL_IDLE;
        default:
            next_rd_line_state = RL_IDLE;
    endcase

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        rd_line_end <= 1'b0;
    else if (I_new_frame)
        rd_line_end <= 1'b0;
    else
        rd_line_end <= rd_line_state == RL_END;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        left_pixels <= 'd0;
    else if (rd_line_start)
        left_pixels <= I_reg_px_width;
    else if (rd_line_state == RL_START)
        left_pixels <= left_pixels - current_length;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        current_length <= 'd0;
    else if (rd_line_state == RL_INIT
        || rd_line_state == RL_POST)
        begin
        if (left_pixels >= MAX_RD_LENTGH)
            current_length <= MAX_RD_LENTGH;
        else
            current_length <= left_pixels;
        end

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_sdm1_vob_req <= 1'b0;
    else if (rd_line_state == RL_IDLE)
        O_sdm1_vob_req <= 1'b0;
    else if (rd_line_state == RL_REQ_MEM && !read_sdm2)
        O_sdm1_vob_req <= 1'b1;
    else if (rd_line_state == RL_POST)
        O_sdm1_vob_req <= 1'b0;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_sdm2_vob_req <= 1'b0;
    else if (rd_line_state == RL_IDLE)
        O_sdm2_vob_req <= 1'b0;
    else if (rd_line_state == RL_REQ_MEM && read_sdm2)
        O_sdm2_vob_req <= 1'b1;
    else if (rd_line_state == RL_POST)
        O_sdm2_vob_req <= 1'b0;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_sdm1_sdram_rd_en <= 1'b0;
    else if (read_sdm2)
        O_sdm1_sdram_rd_en <= 1'b0;
    else if (rd_line_state == RL_START)
        O_sdm1_sdram_rd_en <= 1'b1;
    else if (sdram_wr_rd_end)
        O_sdm1_sdram_rd_en <= 1'b0;
    
always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_sdm1_sdram_start_addr <= 'd0;
    else if (rd_line_start)
        O_sdm1_sdram_start_addr <= line_start_addr + (vib_use_c1 ? FRAME1_START_ADDR : 0);
    else if (rd_line_state == RL_POST)
        O_sdm1_sdram_start_addr <= O_sdm1_sdram_start_addr + current_length;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_sdm1_sdram_length <= 'd0;
    else if (rd_line_state == RL_START)
        O_sdm1_sdram_length <= current_length;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_sdm2_sdram_rd_en <= 1'b0;
    else if (!read_sdm2)
        O_sdm2_sdram_rd_en <= 1'b0;
    else if (rd_line_state == RL_START)
        O_sdm2_sdram_rd_en <= 1'b1;
    else if (sdram_wr_rd_end)
        O_sdm2_sdram_rd_en <= 1'b0;
    
always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_sdm2_sdram_start_addr <= 'd0;
    else if (rd_line_start)
        O_sdm2_sdram_start_addr <= line_start_addr + (vib_use_c1 ? FRAME1_START_ADDR : 0);
    else if (rd_line_state == RL_POST)
        O_sdm2_sdram_start_addr <= O_sdm2_sdram_start_addr + current_length;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_sdm2_sdram_length <= 'd0;
    else if (rd_line_state == RL_START)
        O_sdm2_sdram_length <= current_length;

//--------------------------------------------------------------------
// output
//--------------------------------------------------------------------
always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_out_fifo_wrreq <= 1'b0;
    else if (I_new_frame)
        O_out_fifo_wrreq <= 1'b0;
    else if (rd_line_state == RL_READING)
        O_out_fifo_wrreq <= read_sdm2 ? I_sdm2_sdram_rdata_valid : I_sdm1_sdram_rdata_valid;
    else
        O_out_fifo_wrreq <= 1'b0;

always @(posedge I_sclk)
    O_out_fifo_wdata <= read_sdm2 ? I_sdm2_sdram_rdata : I_sdm1_sdram_rdata;

endmodule

`default_nettype wire
